package com.derbysoft.dswitch.adapter.ihg.dswitch2.translate;

import com.derbysoft.dswitch.adapter.ihg.common.domain.*;
import com.derbysoft.dswitch.adapter.ihg.common.domain.RatePlan;
import com.derbysoft.dswitch.adapter.ihg.common.repository.ChannelAuthorizationRepository;
import com.derbysoft.dswitch.adapter.ihg.common.repository.ConfigRepository;
import com.derbysoft.dswitch.adapter.ihg.common.repository.HotelRepository;
import com.derbysoft.dswitch.adapter.ihg.common.repository.RatePlanRepository;
import com.derbysoft.dswitch.adapter.ihg.common.utils.CollectionUtil;
import com.derbysoft.dswitch.adapter.ihg.common.utils.DateTimeUtils;
import com.derbysoft.dswitch.adapter.ihg.dswitch3.translate.Translator;
import com.derbysoft.dswitch.adapter.ihg.dswitch3.translate.impl.TranslateHelper;
import com.derbysoft.dswitch.adapter.ihg.dto.*;
import com.derbysoft.dswitch.adapter.ihg.exception.ChannelNotFoundException;
import com.derbysoft.dswitch.adapter.ihg.exception.HotelNotFoundException;
import com.derbysoft.remote.dswitch.core.RequestHeader;
import com.derbysoft.remote.dswitch.generic.constants.AgeQualifyingType;
import com.derbysoft.remote.dswitch.generic.constants.SmokingType;
import com.derbysoft.remote.dswitch.generic.dto.SimpleAmountDTO;
import com.derbysoft.remote.dswitch.generic.dto.UniqueIDDTO;
import com.derbysoft.remote.dswitch.hotel.constants.*;
import com.derbysoft.remote.dswitch.hotel.dto.*;
import com.derbysoft.remote.share.Currency;
import com.derbysoft.remote.share.LanguageType;
import org.apache.commons.collections.Predicate;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Created by: jason
 * Date: 2012-05-30
 */
public abstract class BaseTranslator {
    public static final String SYSTEM = "System";
    protected static final String IHG_TAX_INFORMATION = "Tax Information";
    protected static final String IHG_SERVICE_CHARGE_INFORMATION = "Service Charge Information";
    private static final String UNDERLINE = "_";
    private static final String BLANK = "";
    public static final String REQUEST_TYPE = "0";
    public static final String IHG_EXTRA_PERSON_CHARGE = "ExtraPersonChargeInformation";
    public static final String ROOM_TYPE_NAME_FIELD = "room_type_name_field";
    public static final String ADDITIONAL_DETAIL_CODE = "additional_detail_code";
    public static final String ROOM_RATE_DESCRIPTION = "ROOM_RATE_DESCRIPTION";

    @Autowired
    protected HotelRepository hotelMappingService;

    @Autowired
    protected RatePlanRepository ratePlanRepository;

    @Autowired
    protected ConfigRepository configRepository;

    @Value("${dswitch2.adapter}")
    protected String adapter;

    @Value("${provider}")
    protected String provider;

    @Autowired
    protected ChannelAuthorizationRepository channelAuthorizationRepository;

    protected ChannelAuthorization loadChannel(String channelPassport) {
        ChannelAuthorization authorization = channelAuthorizationRepository.loadByDerbyChannelCode(channelPassport);
        if (authorization == null) {
            throw new ChannelNotFoundException(channelPassport);
        }
        return authorization;
    }

    protected void setHeader(Request request, RequestHeader header) {
        request.setPOS(createPOS(header.getSource().getUniqueID().getId()));
        request.setVersion(Translator.VERSION);
    }

    private POS createPOS(String derbyChannelCode) {
        POS pos = new POS();
        Source source = new Source();
        source.setRequestorID(createRequestId(derbyChannelCode));
        pos.setSource(source);
        return pos;
    }

    private RequestorId createRequestId(String derbyChannelCode) {
        RequestorId requestorId = new RequestorId();
        ChannelAuthorization channelAuthorization = channelAuthorizationRepository.loadByDerbyChannelCode(derbyChannelCode.toLowerCase());
        if (channelAuthorization == null) {
            throw new ChannelNotFoundException(derbyChannelCode);
        }
        requestorId.setID(channelAuthorization.getIataNumber());
        requestorId.setType(REQUEST_TYPE);
        return requestorId;
    }

    protected HotelMapping loadHotel(String hotelPassport) {
        HotelMapping hotelMapping = hotelMappingService.loadByDerbyCode(hotelPassport);
        if (hotelMapping != null) {
            return hotelMapping;
        }
        throw new HotelNotFoundException(hotelPassport);
    }


    protected static List<ChargeItemDTO> translateTaxes(Rate rate, Date startDate, Date endDate) {
        if (rate.getFees() == null || CollectionUtil.isEmpty(rate.getFees().getFee())) {
            return new ArrayList<ChargeItemDTO>();
        }
        List<Fee> fees = rate.getFees().getFee();

        List<ChargeItemDTO> chargeItems = new ArrayList<ChargeItemDTO>();
        for (Fee fee : fees) {
            if (!fee.getType().equals(IHG_TAX_INFORMATION) && !fee.getType().equals(IHG_SERVICE_CHARGE_INFORMATION)) {
                continue;
            }
            if (fee.getPercent() != null) {
                ChargeItemDTO chargeItemDTO = new ChargeItemDTO(ChargeType.PERCENT);
                chargeItemDTO.setValue(new BigDecimal(fee.getPercent()).divide(new BigDecimal("100")));
                chargeItemDTO.setDescription(fee.getDescription().getText());
                chargeItems.add(chargeItemDTO);
            }
            if (fee.getAmount() != null) {
                ChargeItemDTO chargeItemDTO = new ChargeItemDTO(ChargeType.FIXED, fee.getAmount(), ChargeUnit.PER_NIGHT);
                chargeItemDTO.setDescription(fee.getDescription().getText());
                chargeItems.add(chargeItemDTO);
            }
        }
        return setStartAndEndDate(chargeItems, startDate, endDate);
    }

    private static List<ChargeItemDTO> setStartAndEndDate(List<ChargeItemDTO> chargeItems, Date startDate, Date endDate) {
        for (ChargeItemDTO chargeItem : chargeItems) {
            chargeItem.setStartDate(startDate);
            chargeItem.setEndDate(endDate);
        }
        return chargeItems;
    }

    protected AvailRoomStayDTO translateAvailRoomStay(final RoomRate roomRate, AvailRoomStay roomStay, RatePlan ratePlan, StayDateRangeDTO stayDateRange) {
        RoomType roomType = (RoomType) CollectionUtil.find(roomStay.getRoomTypes().getRoomType(), new Predicate() {
            @Override
            public boolean evaluate(Object object) {
                return ((RoomType) object).getRoomTypeCode().equalsIgnoreCase(roomRate.getRoomTypeCode());
            }
        });
        AvailRoomStayDTO availRoomStay = new AvailRoomStayDTO();
        Date startDate = stayDateRange.getCheckInDate();
        Date endDate = DateTimeUtils.addDays(stayDateRange.getCheckOutDate(), -1);
        availRoomStay.setRatePlan(translateRatePlan(roomRate, ratePlan, startDate, endDate));
        availRoomStay.setRoomType(translateRoomType(roomType, roomRate));
        availRoomStay.setLanguageType(LanguageType.EN_US);
        availRoomStay.setQuantity(roomRate.getNumberOfUnits());
        availRoomStay.setRoomRate(translateRoomRate(roomRate, roomStay.getTimeSpan()));
        return availRoomStay;
    }

    private String createRoomTypeName(RoomType roomType, String roomTypeCode) {
        if (roomType.getRoomDescription() != null && !StringUtils.isBlank(roomType.getRoomDescription().getText())) {
            return roomType.getRoomDescription().getText();
        }
        return roomTypeCode;
    }

    protected ProviderChainDTO translateProviderChain(final UniqueIDDTO uniqueIDDTO) {
        ProviderChainDTO providerChain = new ProviderChainDTO();
        List<UniqueIDDTO> providers = new ArrayList<UniqueIDDTO>();
        providers.add(uniqueIDDTO);
        providerChain.setProviders(providers);
        return providerChain;
    }

    public static RoomRateDTO translateRoomRate(RoomRate roomRate, TimeSpan timeSpan) {
        RoomRateDTO roomRateDTO = new RoomRateDTO();
        roomRateDTO.setRates(new IhgRates2RateDTOsTranslator(timeSpan).translate(roomRate.getRates().getRate()));
        return roomRateDTO;
    }

    private RoomTypeDTO translateRoomType(RoomType roomType, RoomRate roomRate) {
        RoomTypeDTO roomTypeDTO = new RoomTypeDTO();
        ConfigInfo configInfo = configRepository.loadUsable(ROOM_TYPE_NAME_FIELD);
        if (configInfo.getValue().equals(ROOM_RATE_DESCRIPTION)) {
            roomTypeDTO.setName(getRoomTypeNameByRoomRateDesc(roomType, roomRate));
        } else {
            roomTypeDTO.setName(getRoomTypeNameByAdditionalDesc(roomType, roomType.getRoomTypeCode().replace(roomRate.getRatePlanCode(), "")));
        }
        roomTypeDTO.setCode(roomType.getRoomTypeCode().replace(roomRate.getRatePlanCode(), ""));
        return roomTypeDTO;
    }

    private String getRoomTypeNameByRoomRateDesc(RoomType roomType, RoomRate roomRate) {
        RoomRateDescription description = roomRate.getRoomRateDescription();
        if (description != null && !StringUtils.isBlank(description.getText())) {
            return description.getText();
        }
        return getRoomTypeNameByAdditionalDesc(roomType, roomType.getRoomTypeCode().replace(roomRate.getRatePlanCode(), ""));
    }

    private String getRoomTypeNameByAdditionalDesc(RoomType roomType, String roomTypeCode) {
        ConfigInfo configInfo = configRepository.loadUsable(HotelAvailRSTranslator.ADDITIONAL_DETAIL_CODE);
        if (configInfo == null || !configInfo.isUsable()) {
            return createRoomTypeName(roomType, roomTypeCode);
        }
        if (roomType.getAdditionalDetails() != null && !roomType.getAdditionalDetails().getAdditionalDetail().isEmpty()) {
            for (AdditionalDetail additionalDetail : roomType.getAdditionalDetails().getAdditionalDetail()) {
                if (additionalDetail.getCode() != null
                        && additionalDetail.getCode().equals(configInfo.getValue())
                        && additionalDetail.getDetailDescription() != null
                        && !additionalDetail.getDetailDescription().getText().isEmpty()) {
                    return additionalDetail.getDetailDescription().getText();
                }
            }
        }
        return createRoomTypeName(roomType, roomTypeCode);
    }

    public static RatePlanDTO translateRatePlan(RoomRate roomRate, RatePlan ratePlan, Date startDate, Date endDate) {
        RatePlanDTO ratePlanDTO = createRatePlan(ratePlan);
        Rate ihgRate = roomRate.getRates().getRate().get(0);

        ratePlanDTO.setCode(roomRate.getRatePlanCode());
        ratePlanDTO.setTaxes(translateTaxes(ihgRate, startDate, endDate));
        ratePlanDTO.setAvailGuarantees(translateAvailGuarantees());
        ratePlanDTO.setCancelPolicy(translateCancelPolicy(ihgRate));
        ratePlanDTO.setExtraItemCharges(translateExtraItemCharges(ihgRate));
        return ratePlanDTO;
    }

    private static List<ExtraItemChargeDTO> translateExtraItemCharges(Rate rate) {
        List<ExtraItemChargeDTO> extraItemCharges = new ArrayList<ExtraItemChargeDTO>();

        if (rate.getAdditionalGuestAmounts() != null &&
                CollectionUtil.isNotEmpty(rate.getAdditionalGuestAmounts().getAdditionalGuestAmount())) {
            SimpleAmountDTO additionalAmount = null;
            int quantity = 0;
            for (AdditionalGuestAmount additionalGuestAmount : rate.getAdditionalGuestAmounts().getAdditionalGuestAmount()) {
                if (additionalGuestAmount.getType().equals(IHG_EXTRA_PERSON_CHARGE)) {
                    quantity++;
                    if (additionalGuestAmount.getAgeQualifyingCode().equals(AgeQualifyingType.ADULT.getCode())) {
                        additionalAmount = new SimpleAmountDTO(additionalGuestAmount.getAmount().getAmountBeforeTax(),
                                Currency.fromCode(additionalGuestAmount.getAmount().getCurrencyCode()));
                    }
                }
            }
            if (additionalAmount != null) {
                ExtraItemChargeDTO extraItemChargeDTO = new ExtraItemChargeDTO();
                extraItemChargeDTO.setDescription(IHG_EXTRA_PERSON_CHARGE);
                extraItemChargeDTO.setQuantity(quantity);
                extraItemChargeDTO.setAmount(additionalAmount);
                extraItemChargeDTO.setExtraItemType(ExtraItemType.BED);
                extraItemCharges.add(extraItemChargeDTO);
            }
        }
        return extraItemCharges;
    }

    private static RatePlanDTO createRatePlan(RatePlan ratePlan) {
        RatePlanDTO ratePlanDTO = new RatePlanDTO();
        ratePlanDTO.setName(ratePlan.getName());
        ratePlanDTO.setPaymentType(translatePayment(ratePlan.getPaymentType()));
        ratePlanDTO.setRateType(RateType.CASH);
        ratePlanDTO.setNeedGuarantee(ratePlan.getNeedGuarantee());
        ratePlanDTO.setFreeMeal(translateFreeMeal(ratePlan.getFreeMeal()));
        return ratePlanDTO;
    }

    private static PaymentType translatePayment(PaymentType paymentType) {
        if (paymentType == PaymentType.POA) {
            return PaymentType.POA;
        }
        if (paymentType == PaymentType.PREPAY) {
            return PaymentType.PREPAY;
        }
        return PaymentType.UNKNOWN;
    }

    private static FreeMealDTO translateFreeMeal(FreeMeal freeMeal) {
        FreeMealDTO freeMealDTO = new FreeMealDTO();
        freeMealDTO.setType(freeMeal.getType());
        freeMealDTO.setDescription(freeMeal.getDescription());
        freeMealDTO.setQuantity(freeMeal.getQuantity());
        freeMealDTO.setUnit(FreeMealUnit.PER_STAY);
        return freeMealDTO;
    }

    private static CancelPolicyDTO translateCancelPolicy(Rate ihgRate) {
        if (CollectionUtil.isEmpty(ihgRate.getCancelPolicies().getCancelPenalty())
                || ihgRate.getCancelPolicies().getCancelPenalty().size() != 1) {
            return null;
        }
        CancelPenalty cancelPenalty = ihgRate.getCancelPolicies().getCancelPenalty().get(0);
        if (CollectionUtil.isEmpty(cancelPenalty.getPenaltyDescription())) {
            return null;
        }
        CancelPolicyDTO cancelPolicyDTO = new CancelPolicyDTO();
        cancelPolicyDTO.setCancelPenaltyType(CancelPenaltyType.UNKNOWN);
        cancelPolicyDTO.setDescription(createDesc(cancelPenalty.getPenaltyDescription()));
        if (cancelPenalty.getDeadline() != null) {
            cancelPolicyDTO.setDeadline(cancelPenalty.getDeadline().getOffsetUnitMultiplier()
                    + UNDERLINE + cancelPenalty.getDeadline().getOffsetTimeUnit());
        }
        return cancelPolicyDTO;
    }

    private static String createDesc(List<Description> descriptions) {
        if (!descriptions.isEmpty()) {
            return descriptions.get(0).getText();
        }
        return BLANK;
    }

    private static List<AvailGuaranteeDTO> translateAvailGuarantees() {
        return new ArrayList<AvailGuaranteeDTO>();
    }

    protected RatePlanCandidates createRatePlanCandidates(String channelPassport) {
        RatePlanCandidates ratePlanCandidates = new RatePlanCandidates();
        List<RatePlan> ratePlans = loadChannel(channelPassport).getRatePlans();
        if (CollectionUtil.isNotEmpty(ratePlans)) {
            for (RatePlan ratePlan : ratePlans) {
                ratePlanCandidates.getRatePlanCandidate().add(createRatePlanCandidate(ratePlan.getCode()));
            }
        }
        return ratePlanCandidates;
    }


    protected HotelSearchCriteria createHotelSearchCriteria(String hotelPassport) {
        HotelSearchCriteria hotelSearchCriteria = new HotelSearchCriteria();
        Criterion criterion = new Criterion();
        HotelRef hotelRef = new HotelRef();
        hotelRef.setHotelCode(loadHotel(hotelPassport).getCooperatorCode());
        criterion.setHotelRef(hotelRef);
        hotelSearchCriteria.setCriterion(criterion);
        return hotelSearchCriteria;
    }

    private RatePlanCandidate createRatePlanCandidate(String ratePlanCode) {
        RatePlanCandidate ratePlanCandidate = new RatePlanCandidate();
        ratePlanCandidate.setRatePlanCode(ratePlanCode);
        return ratePlanCandidate;
    }

    protected List<RoomStayCandidate> translateRoomStayCandidates(RoomStayCandidateDTO roomStayCandidateDTO) {
        List<RoomStayCandidate> roomStayCandidates = new ArrayList<RoomStayCandidate>();
        RoomStayCandidate roomStayCandidate = new RoomStayCandidate();
        roomStayCandidate.setGuestCounts(translateGuestCounts(roomStayCandidateDTO.getGuestCounts()));
        Boolean nonSmoking = translateSmoking(roomStayCandidateDTO.getSmoking());
        if (nonSmoking != null) {
            roomStayCandidate.setNonSmoking(nonSmoking);
        }
        roomStayCandidate.setQuantity(roomStayCandidateDTO.getNumberOfUnits());
        roomStayCandidates.add(roomStayCandidate);
        return roomStayCandidates;
    }

    private Boolean translateSmoking(SmokingType smokingType) {
        if (SmokingType.NONSMOKING == smokingType) {
            return true;
        }
        if (SmokingType.SMOKING == smokingType) {
            return false;
        }
        return null;
    }

    private GuestCounts translateGuestCounts(List<GuestCountDTO> guestCountsDTO) {
        GuestCounts result = new GuestCounts();
        List<GuestCount> counts = new ArrayList<GuestCount>();
        counts.add(TranslateHelper.createAdultGuestCount(getGuestCount(guestCountsDTO, AgeQualifyingType.ADULT)));
        if (getGuestCount(guestCountsDTO, AgeQualifyingType.CHILD) != null) {
            counts.add(TranslateHelper.createChildGuestCount(getGuestCount(guestCountsDTO, AgeQualifyingType.CHILD)));
        }
        result.getGuestCount().addAll(counts);
        result.setIsPerRoom(false);
        return result;
    }

    private Integer getGuestCount(List<GuestCountDTO> guestCountsDTO, final AgeQualifyingType adult) {
        GuestCountDTO guestCountDTO = (GuestCountDTO) CollectionUtil.find(guestCountsDTO, new Predicate() {
            @Override
            public boolean evaluate(Object object) {
                return adult == ((GuestCountDTO) object).getAgeQualifyingType() && ((GuestCountDTO) object).getCount() != 0;
            }
        });
        if (guestCountDTO == null) {
            return null;
        }
        return guestCountDTO.getCount();
    }

    protected StayDateRange translateStayDateRange(StayDateRangeDTO stayDateRange) {
        StayDateRange result = new StayDateRange();
        result.setStart(DateTimeUtils.formatDate(stayDateRange.getCheckInDate()));
        result.setEnd(DateTimeUtils.formatDate(stayDateRange.getCheckOutDate()));
        return result;
    }
}
